package service

import (
	"database/sql"
	"fmt"
	"github.com/rs/xid"
	"math"
	"strconv"
	"strings"
	"time"
	"zimuzu_cvn_web_api/config"
	"zimuzu_cvn_web_api/lib/grpcClient"
	"zimuzu_cvn_web_api/model"
	"zimuzu_cvn_web_api/pkg/drive/redis"
	. "zimuzu_cvn_web_api/pkg/e"
	"zimuzu_cvn_web_api/pkg/errno"
	"zimuzu_cvn_web_api/service/cache"
)

var (
	cvnCharges = []float64{3, 5, 7}
)

//获取钱包地址
//Auth: TXG
func GetWalletAddress(uid int64) (address string, err error) {
	coinType := COIN_TYPE_ERC20
	ok, err := model.ExistUserWalletAddress(uid)
	if err != nil {
		return "", err
	}
	if ok {
		AddressCheckData := map[string]string{"uid": strconv.FormatInt(uid, 10), "coin_type": coinType}
		AddressCheckResp, err := grpcClient.AddressCheck(AddressCheckData)
		if err != nil {
			return "", err
		}
		if !AddressCheckResp.Ok {
			err = errno.DbSignError
			return "", err
		}
		userWalletAddress, err := model.GetUserWalletAddress(uid)
		if err != nil {
			return "", err
		}
		address = userWalletAddress.Address
		if ok := CheckWalletFormat(address, coinType); !ok {
			err = errno.ErrWalletFormat
			return "", err
		}
	} else {
		res, err := model.GatewayRequest("GET", ApiGetAddress, nil, nil)
		if err != nil {
			return "", err
		}
		address = res.Data.Result.Address
		if ok := CheckWalletFormat(address, coinType); !ok {
			err = errno.ErrWalletFormat
			return "", err
		}
		AddressCreateData := map[string]string{"uid": strconv.FormatInt(uid, 10), "coin_type": coinType, "address": address}
		AddressCreateResp, err := grpcClient.AddressCreate(AddressCreateData)
		if err != nil {
			return "", err
		}
		userWalletAddress := model.UserWalletAddressDb{
			Uid:         uid,
			CoinType:    coinType,
			Address:     address,
			SignString:  AddressCreateResp.SignString,
			SignVersion: AddressCreateResp.SignVersion,
		}
		err = model.AddUserWalletAddress(&userWalletAddress)
		if err != nil {
			return "", err
		}
	}
	return address, nil
}

//获取手续费
func GetCharges() []float64 {
	return cvnCharges
}

//获取账户余额
func GetAmountBalance(uid int64) (re model.AmountBalance, err error) {
	userMobile, err := model.GetUserMobile(config.C.DB.DB, uid)
	if err != nil {
		err = errno.GetDataErr
		return re, err
	}
	cvnAmount, err := model.GetCvnAmount(config.C.DB.DB, uid)
	if err != nil {
		err = errno.GetDataErr
		return re, err
	}
	re.Area = userMobile.Area
	re.Mobile = userMobile.Mobile
	re.CvnBalance = Decimal(cvnAmount.Balance, COIN_DECIMAL_CVNT)
	return re, nil
}

//校验支付密码
func CheckPayPassword(uid int64, password string) (u string, err error) {
	var (
		ErrCacheKey, BanCacheKey, CheckCacheKey string
	)
	password = strings.ToLower(password)
	if password == EmptyStrMd5 {
		err := errno.EmptyMd5PayPassword
		return "", err
	}
	userAmount, err := model.GetUserAmount(uid)
	if err != nil {
		err = errno.GetDataErr
		return "", err
	}
	if userAmount.PayPassword == "" {
		err = errno.EmptyPayPassword
		return "", err
	}
	BanCacheKey = cache.GetPayPasswordBanKey(uid)
	if ok := redis.Exists(BanCacheKey); ok {
		err = errno.PayPasswordFreeze
		return "", err
	}
	ErrCacheKey = cache.GetPayPasswordErrKey(uid)
	encryptPassword := EncryptPayPassword(password, userAmount.Salt)
	if encryptPassword != userAmount.PayPassword {
		errTime, _ := redis.Incr(ErrCacheKey)
		ok, err := redis.Expire(ErrCacheKey, 600)
		if err != nil || !ok {
			err = errno.UpdateDataErr
			return "", err
		}
		if errTime >= 5 {
			ok, err := redis.Set(BanCacheKey, 1, 10800)
			if err != nil || !ok {
				err = errno.UpdateDataErr
				return "", err
			}
			err = errno.PayPasswordFreezeTip
			return "", err
		} else {
			err = errno.ErrPayPassword
			return "", err
		}
	}
	u = xid.New().String()
	CheckCacheKey = cache.GetPayPasswordCheckKey(uid, u)
	ok, err := redis.Set(CheckCacheKey, 1, 300)
	if err != nil || !ok {
		err = errno.UpdateDataErr
		return "", err
	}
	redis.UnDelete(ErrCacheKey)
	return u, nil
}

// 设置支付密码
// Auth: hzp
func SetPayPassword(uid int64, password string) (err error) {

	userAmount, err := model.GetUserAmount(uid)
	if err != nil {
		return err
	}

	checkPayPassword := EncryptPayPassword(password, userAmount.Salt)
	if userAmount.PayPassword == checkPayPassword {
		return nil
	}

	err = model.UpdatePayPassword(config.C.DB.DB, uid, checkPayPassword)
	if err != nil {
		return err
	}
	return nil
}

//转账提交
//Auth: TXG
func Transfer(uid int64, author string, params model.TransferParam) (id int64, err error) {
	var (
		status      int
		charge      float64
		banCacheKey string
		post        = make(map[string]string)
	)
	if config.C.Keys.TransferKey != "AC3FF6E4E68B063D" {
		if params.Value > 1000 {
			err = errno.ErrTransValue
			return 0, err
		}
	}
	//校验参数
	if params.Value <= 0 {
		err = errno.ParamsError
		return 0, err
	}
	//检查是否为站内钱包
	toUserWalletAddress, isIntraAddress, err := GetCheckAddress(params.To)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	if uid == toUserWalletAddress.Uid {
		err = errno.ErrTransToSelf
		return 0, err
	}
	if isIntraAddress {
		charge = 0
	} else {
		if params.Speed < 0 || params.Speed >= len(cvnCharges) {
			err = errno.ParamsError
			return 0, err
		}
		charge = cvnCharges[params.Speed]
		if charge <= 0 {
			err = errno.SystemErr
			return 0, err
		}
	}
	amount := Decimal(params.Value-charge, COIN_DECIMAL_CVNT)
	if amount <= 0 {
		err = errno.ErrLessCharge
		return 0, err
	}
	//检查转账是否开放
	cvnTransferOn, err := model.GetGlobalVarValue(config.C.DB.DB, 16)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	if cvnTransferOn != "1" {
		err = errno.CloseTransferTip
		return 0, err
	}
	//检查账号是否在黑名单
	ok, err := model.IsBlackUser(config.C.DB.DB, uid)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	if ok {
		err = errno.BlackUser
		return 0, err
	}
	//检查账户是否被冻结
	userAmount, err := model.GetUserAmount(uid)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	if userAmount.Status == 0 {
		err = errno.PayPasswordFreeze
		return 0, err
	}
	//支付密码错误次数过多冻结
	banCacheKey = cache.GetPayPasswordBanKey(uid)
	if ok := redis.Exists(banCacheKey); ok {
		err = errno.PayPasswordFreeze
		return 0, err
	}
	//检查是否绑定手机号
	userMobile, err := model.GetUserMobile(config.C.DB.DB, uid)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	if userMobile.Area == 0 || userMobile.Mobile == "" {
		err = errno.NotBindMobile
		return 0, err
	}
	//检查是否免验证码
	smsCodeChecked, err := IsTransferSmsCodeChecked(uid, author)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	//检查验证码是否正确
	if !smsCodeChecked {
		if errValid := ValidateCode(userMobile.Mobile, SendCodeSceneTransfer, params.Code); errValid != nil {
			err = errValid
			return 0, err
		}
		//设置短信验证码已校验缓存
		CodeCheckedCacheKey := cache.GetTransferSmsCodeCheckedKey(uid, author)
		ok, err = redis.Set(CodeCheckedCacheKey, 1, 1800)
		if err != nil || !ok {
			err = errno.UpdateDataErr
			return 0, err
		}
	}
	//检查支付密码是否校验成功
	if ok = IsPayPasswordChecked(uid, params.U); !ok {
		err = errno.PayPasswordCheckExpire
		return 0, err
	}
	DeleteU(uid, params.U)
	//付款人地址
	from, err := GetWalletAddress(uid)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	notExamine, err := CvnNotExamine(uid, amount)
	if err != nil {
		err = errno.GetDataErr
		return 0, err
	}
	//提交事务
	if isIntraAddress {
		if notExamine {
			status = model.DealStatusSuccess
		} else {
			status = model.DealStatusCheck
		}
		tx, err := config.C.DB.DB.Beginx()
		if err != nil {
			err = errno.UpdateDataErr
			return 0, err
		}
		//付款账户扣款，记录CVN流水
		updateAmountData := UpdateCvnAmountParams{
			Uid:    uid,
			Amount: -amount,
			Source: model.LogSourceInnerOut,
			From:   from,
			To:     params.To,
			Status: status,
		}
		id, err = UpdateCvnAmountBalance(tx, updateAmountData)
		if err != nil {
			_ = tx.Rollback()
			return 0, err
		}
		if notExamine {
			//收款账户收款、记录CVN流水
			updateInnerUserAmountData := UpdateCvnAmountParams{
				Uid:         toUserWalletAddress.Uid,
				Amount:      amount,
				Source:      model.LogSourceInnerIn,
				RelateId:    strconv.FormatInt(id, 10),
				RelateTable: TableCvnLog,
				From:        from,
				To:          params.To,
			}
			_, err = UpdateCvnAmountBalance(tx, updateInnerUserAmountData)
			if err != nil {
				_ = tx.Rollback()
				return 0, err
			}
		} else {
			cvnExamine := model.CvnExamine{
				Uid:   uid,
				LogId: id,
			}
			err = model.AddCvnExamine(tx, cvnExamine)
			if err != nil {
				_ = tx.Rollback()
				err = errno.UpdateDataErr
				return 0, err
			}
		}
		if err = tx.Commit(); err != nil {
			err = errno.UpdateDataErr
			return 0, err
		}
		return id, nil
	} else {
		tx, err := config.C.DB.DB.Beginx()
		if err != nil {
			err = errno.UpdateDataErr
			return 0, err
		}
		//账户扣款，记录CVN流水
		updateAmountData := UpdateCvnAmountParams{
			Uid:    uid,
			Amount: -amount,
			Source: model.LogSourceOut,
			From:   from,
			To:     params.To,
			Status: model.DealStatusCheck,
		}
		id, err = UpdateCvnAmountBalance(tx, updateAmountData)
		if err != nil {
			_ = tx.Rollback()
			return 0, err
		}
		//手续费扣款、记录CVN流水
		chargeLogData := UpdateCvnAmountParams{
			Uid:         uid,
			Amount:      -charge,
			Source:      model.LogSourceOutFee,
			RelateId:    strconv.FormatInt(id, 10),
			RelateTable: TableCvnLog,
		}
		_, err = UpdateCvnAmountBalance(tx, chargeLogData)
		if err != nil {
			_ = tx.Rollback()
			return 0, err
		}
		if !notExamine {
			cvnExamine := model.CvnExamine{
				Uid:   uid,
				LogId: id,
			}
			err = model.AddCvnExamine(tx, cvnExamine)
			if err != nil {
				_ = tx.Rollback()
				err = errno.UpdateDataErr
				return 0, err
			}
		}
		if err = tx.Commit(); err != nil {
			err = errno.UpdateDataErr
			return 0, err
		}
		if notExamine {
			post["amount"] = fmt.Sprintf("%.0f", amount*math.Pow(10, COIN_DECIMAL_CVNT))
			post["coin"] = COIN_NAME_CVNT
			post["address"] = params.To
			post["requestId"] = COIN_NAME_CVNT + "_" + strconv.FormatInt(id, 10)
			_, _ = model.GatewayRequest("POST", ApiTransfer, post, nil)
		}
		return id, nil
	}
}

//检查支付密码是否校验成功
//Auth: TXG
func IsPayPasswordChecked(uid int64, u string) bool {
	cacheKey := cache.GetPayPasswordCheckKey(uid, u)
	if ok := redis.Exists(cacheKey); !ok {
		return false
	}
	check, err := redis.Get(cacheKey)
	if err != nil {
		Log("info", err)
		return false
	}
	if check != "1" && check != u {
		return false
	}
	return true
}

//检验是否已设置支付密码
//Auth: Xu
func CheckPayPasswordExist(uid int64) (bo bool, err error) {
	userAmount, err := GetUserAmountByUid(uid)
	if err != nil {
		return false, err
	}

	return userAmount.PayPassword != "", nil
}

//是否为站内钱包
//Auth: TXG
func GetCheckAddress(address string) (re model.UserWalletAddressDb, ok bool, err error) {
	if ok := CheckWalletFormat(address, COIN_TYPE_ERC20); !ok {
		err := errno.ErrWalletFormat
		Log("err", err)
		return re, false, err
	}
	re, err = model.GetUserWalletAddressByAddress(address)
	if err == sql.ErrNoRows {
		return re, false, nil
	}
	if err != nil {
		err = errno.GetDataErr
		return re, false, err
	}
	if re.Status != 1 {
		err = errno.InvalidAddress
		Log("err", err)
		return re, false, err
	}
	return re, true, nil
}

//检查转账是否免验证码
func IsTransferSmsCodeChecked(uid int64, author string) (bool, error) {
	return false, nil
	cacheKey := cache.GetTransferSmsCodeCheckedKey(uid, author)
	if ok := redis.Exists(cacheKey); !ok {
		return false, nil
	}
	check, err := redis.Get(cacheKey)
	if err != nil {
		return false, err
	}
	if check != "1" {
		return false, nil
	}
	return true, nil
}

//检查是否免审核
func CvnNotExamine(uid int64, amount float64) (bool, error) {
	nowTime := time.Now().Unix()
	amount = math.Abs(amount)
	//触发条件：单笔限额
	if amount > CvnExamineSingleQuota {
		return false, nil
	}
	//触发条件：存在审核中记录
	ok, err := model.ExistCvnExamine(config.C.DB.DB, uid)
	if err != nil {
		return false, err
	}
	if ok {
		return false, nil
	}
	//触发条件：新地址
	userWalletAddress, err := model.GetUserWalletAddress(uid)
	if err != nil {
		return false, err
	}
	if nowTime-userWalletAddress.CreateTime < TimeFormatWeek && amount > CvnExamineNewQuota {
		return false, nil
	}
	//获取充值总金额
	rechargeSum, err := model.SumCvnLogSource(config.C.DB.DB, uid, model.LogSourceIn, 0)
	if err != nil {
		return false, err
	}
	//触发条件：小额充值
	if rechargeSum < CvnExamineSmallRecharge && amount > CvnExamineSmallRechargeQuota {
		return false, nil
	}
	//触发条件：转账超过充值金额
	if rechargeSum < amount && amount > CvnExamineExceedQuota {
		return false, nil
	}
	//触发条件：日限额
	daySum, err := model.SumCvnLogSource(config.C.DB.DB, uid, model.LogSourceOut, 1)
	if err != nil {
		return false, err
	}
	daySum = math.Abs(daySum)
	if daySum+amount > CvnExamineDayQuota {
		return false, nil
	}
	//触发条件：周限额
	weekSum, err := model.SumCvnLogSource(config.C.DB.DB, uid, model.LogSourceOut, 7)
	if err != nil {
		return false, err
	}
	weekSum = math.Abs(weekSum)
	if weekSum+amount > CvnExamineWeekQuota {
		return false, nil
	}
	//触发条件：月限额
	monthSum, err := model.SumCvnLogSource(config.C.DB.DB, uid, model.LogSourceOut, 30)
	if err != nil {
		return false, err
	}
	monthSum = math.Abs(monthSum)
	if monthSum+amount > CvnExamineMonthQuota {
		return false, nil
	}
	return true, nil
}
